// SPDX-License-Identifier: GPL-2.0
#include <linux/kernel.h>
#include "ax_utils.h"
#include "axvu.h"
#include "axvu_regs.h"

#ifndef VX_CMDQ

#define DEBUG_INFO_CFG 0

/* Functional modules */
static int  AXVU_CalcStride(int width, AXVU_FMT_E fmt,
		int byte_cnt, int align_byte)
{
	int stride, temp;
	int rnd_byte = align_byte - 1;

	stride = 0;
	switch (fmt) {
	case ARGB:
	case ARGB_WHC:
		stride = (width * 4 * byte_cnt + rnd_byte) /
			align_byte * align_byte;
		break;
	case YUV420:
	case YVU420:
	case GRAY:
	case GRAY_WHC:
		/* stride of UV equal to stride of Y */
		stride = (width * byte_cnt + rnd_byte) /
			align_byte * align_byte;
		break;
	case RGB:
	case RGB_WHC:
		/*
		 *store 5 pixel in every 16 bytes
		 *(for 8-bit, 16-bit not support yet)
		 */
		temp = (width * 3 + 14) / 15;
		stride = (temp + (temp % (align_byte / 16))) * 16;
		break;
	case STEREO_ARGB:
	case STEREO_ARGB_WHC:
		stride = (width * 8 * byte_cnt + rnd_byte) /
			align_byte * align_byte;
		break;
	default:
		ax_err(1, "format error!\n");
		break;
	}

	return stride;
}

static void AXVU_SetRegRszMerge(unsigned int merge_mode,
				unsigned int src_stride_2nd,
				unsigned int dst_stride,
				unsigned int src_2nd_data_addr,
				unsigned int *register_group)
{
	AXVU_MERGE_CFG mergecfg;
	AXVU_MERGE_STRIDE mergestride;
	AXVU_RSZ_ADDR inaddr2nd;

	mergecfg.dwval = 0;
	mergestride.dwval = 0;
	inaddr2nd.dwval = 0;

	mergecfg.bits.merge_mode = merge_mode - 1;
	mergestride.bits.instride2nd = src_stride_2nd >> 5;
	mergestride.bits.outstride = dst_stride >> 5;
	inaddr2nd.bits.addr = src_2nd_data_addr;

	register_group[0] = mergecfg.dwval;
	register_group[1] = mergestride.dwval;
	register_group[2] = inaddr2nd.dwval;

}

static void AXVU_RszParaConvertInput(int src_w, int src_h, int ofs_x, int ofs_y,
	int crop_w, int crop_h,
	AXVU_FMT_E infmt, AXVU_FMT_E outfmt,
	int byte_cnt, int out_byte_cnt, int in_align_byte,
	unsigned int src_addr0, unsigned int src_addr1,
	int *src_stride,
	int *oft_x_align, int *oft_y_align,
	unsigned int *crop_addr0, unsigned int *crop_addr1,
	int *crop_w_align, int *crop_h_align,
	int *crop_w_align_byte, int *crop_w_align_csc_byte,
	int *oft_x_left, int *oft_y_left, int *crop_w1_byte)
{

	int src_w1, src_h1, ofs_x1, ofs_y1, crop_w1, crop_h1;
	int oft_x_align_byte = 0, oft_x_left_byte = 0;

	/* reverse W and H for WHC format */
	src_w1 = (infmt & WHC_MASK) ? src_h : src_w;
	src_h1 = (infmt & WHC_MASK) ? src_w : src_h;
	ofs_x1 = (infmt & WHC_MASK) ? ofs_y : ofs_x;
	ofs_y1 = (infmt & WHC_MASK) ? ofs_x : ofs_y;
	crop_w1 = (infmt & WHC_MASK) ? crop_h : crop_w;
	crop_h1 = (infmt & WHC_MASK) ? crop_w : crop_h;

	/* source buffer stride */
	*src_stride = AXVU_CalcStride(src_w1, infmt, byte_cnt, in_align_byte);

	if (DEBUG_INFO_CFG) {
		ax_err(1, "Source size= %d, %d. Crop window = %d, %d, %d, %d\n",
			src_w1, src_h1, ofs_x1, crop_w1, ofs_y1, crop_h1);
		ax_err(1, "src_stride = %d, format = %d, src_w1 = %d.\n",
				*src_stride, infmt, src_w1);
	}
	/*
	 *crop with ddr align
	 *x dir crop for align crop :
	 *	oft_x_align / crop_w_align (with Unit pixel)
	 *y dir crop for align crop :
	 *	oft_y_align / crop_h_align (with Unit pixel)
	 */

	switch (infmt) {
	case ARGB:
	case ARGB_WHC:
		*oft_x_align = (ofs_x1 * 4 * byte_cnt / in_align_byte) *
			in_align_byte / byte_cnt / 4;
		*crop_w_align = crop_w1 + ofs_x1 - *oft_x_align;
		*oft_x_left = ofs_x1 - *oft_x_align;

		*crop_w_align_byte = (*crop_w_align) * 4 * byte_cnt;
		oft_x_left_byte = (*oft_x_left) * 4 * byte_cnt;
		oft_x_align_byte = (*oft_x_align) * 4 * byte_cnt;

		*oft_y_align = ofs_y1;
		*crop_h_align = crop_h1;
		break;
	case YUV420:
	case YVU420:
		*oft_x_align = (ofs_x1 * byte_cnt / in_align_byte) *
				in_align_byte / byte_cnt;
		*crop_w_align = ((crop_w1 + ofs_x1 - *oft_x_align) + 1) / 2 * 2;
		*oft_x_left = ofs_x1 - *oft_x_align;

		oft_x_left_byte = (*oft_x_left) * byte_cnt;
		*crop_w_align_byte = (*crop_w_align) * byte_cnt;
		oft_x_align_byte = (*oft_x_align) * byte_cnt;

		*oft_y_align = ofs_y1 / 2 * 2;
		*crop_h_align = (crop_h1 + ofs_y1 - *oft_y_align + 1) / 2 * 2;
		break;
	case GRAY:
	case GRAY_WHC:
		// ! TBD: 16-bit gray x direction offset
		//16-aligned need ( not 32) ? - yes
		*oft_x_align = (ofs_x1 * byte_cnt / in_align_byte) *
				in_align_byte / byte_cnt;
		*crop_w_align = ((crop_w1 + ofs_x1 - *oft_x_align) + 1) / 2 * 2;
		*oft_x_left = ofs_x1 - *oft_x_align;

		oft_x_left_byte = (*oft_x_left) * byte_cnt;
		*crop_w_align_byte = (*crop_w_align) * byte_cnt;
		oft_x_align_byte = (*oft_x_align) * byte_cnt;
		/* ! TBD: gray y direction offset 2-aligned need? - no */
		*oft_y_align = ofs_y1;
		*crop_h_align = crop_h1;
		break;
	case RGB:
	case RGB_WHC:
		break;
	default:
		break;
	}

	switch (outfmt) {
	case ARGB:
	case ARGB_WHC:
		*crop_w1_byte = crop_w1 * 4 * byte_cnt;
		*crop_w_align_csc_byte = (*crop_w_align) * 4 * byte_cnt;
		break;
	case YUV420:
	case YVU420:
		*crop_w1_byte = crop_w1 * byte_cnt;
		*crop_w_align_csc_byte = (*crop_w_align) * byte_cnt;
		break;
	case GRAY:
	case GRAY_WHC:
		*crop_w1_byte = crop_w1 * byte_cnt;
		*crop_w_align_csc_byte = (*crop_w_align) * byte_cnt;
		break;
	case RGB:
	case RGB_WHC:
		break;
	default:
		break;
	}

	*oft_y_left = ofs_y1 - *oft_y_align;

	*crop_addr0 = src_addr0 + oft_x_align_byte +
			(*oft_y_align) * (*src_stride);
	*crop_addr1 = src_addr1 + oft_x_align_byte +
			(*oft_y_align) * (*src_stride) / 2;

	if (DEBUG_INFO_CFG) {
		ax_err(1, "Software crop window : x = %d, %d, y = %d, %d.\n",
			*oft_x_align, *crop_w_align,
			*oft_y_align, *crop_h_align);
		ax_err(1, "Hardware crop window : x = %d, %d, y = %d, %d.\n",
			*oft_x_left, crop_w1,
			*oft_y_left, crop_h1);
	}
}


static void AXVU_RszParaConvertOutput(int dst_w, int dst_h, AXVU_FMT_E outfmt,
	int byte_cnt, int out_align_byte,
	int *dst_stride)
{
	int dst_w1, dst_h1;

	/* reverse W and H for WHC format */
	dst_w1 = (outfmt & WHC_MASK) ? dst_h : dst_w;
	dst_h1 = (outfmt & WHC_MASK) ? dst_w : dst_h;

	/* source buffer stride */
	*dst_stride = AXVU_CalcStride(dst_w1, outfmt, byte_cnt, out_align_byte);

	if (DEBUG_INFO_CFG) {
		ax_err(1, "Dst size = %d, %d\n", dst_w1, dst_h1);
		ax_err(1, "dst_stride = %d, format = %d, dst_w1 = %d.\n",
				*dst_stride, outfmt, dst_w1);
	}
}

void AXVU_SetRegRszBatch(int src_width, int src_height, AXVU_FMT_E src_fmt,
	int y_stride, int uv_stride,
	int dst_width, int dst_height, AXVU_FMT_E dst_fmt, int dst_stride_en,
	int dst_stride, float scalex, float scaley,
	int offset_x, int offset_y, bool whc_flag,
	int yuv_limit_range, int inbw, int outbw,
	unsigned int src_data_addr, unsigned int src_uv_data_addr,
	unsigned int dst_data_addr, unsigned int dst_uv_data_addr,
	unsigned int hist_roi_x, unsigned int hist_roi_y,
	unsigned int hist_roi_x_end, unsigned int hist_roi_y_end,
	unsigned int hist_result_addr, unsigned int *register_group)
{
	AXVU_RSZ_ADDR  inaddr0, inaddr1, outaddr0, outaddr1, histaddr;
	AXVU_RSZ_IN_SIZE insize;
	AXVU_RSZ_OUT_SIZE outsize;
	AXVU_RSZ_IN_STRIDE instride;
	AXVU_RSZ_CONVERT_CFG convertcfg;
	AXVU_RSZ_SCALEFAC facw, fach;
	AXVU_RSZ_OUT_STRIDE outstride;
	AXVU_HIST_ROI_START histroi0;
	AXVU_HIST_ROI_END histroi1;

	inaddr0.dwval = 0;
	inaddr1.dwval = 0;
	outaddr0.dwval = 0;
	outaddr1.dwval = 0;
	insize.dwval = 0;
	outsize.dwval = 0;
	instride.dwval = 0;
	convertcfg.dwval = 0;
	facw.dwval = 0;
	fach.dwval = 0;
	outstride.dwval = 0;
	histroi0.dwval = 0;
	histroi1.dwval = 0;
	histaddr.dwval = 0;

	inaddr0.bits.addr = src_data_addr;
	inaddr1.bits.addr = src_uv_data_addr;
	outaddr0.bits.addr = dst_data_addr;
	outaddr1.bits.addr = dst_uv_data_addr;

	insize.bits.width = src_width - 1 +
			((src_fmt & WHC_MASK) ? 0 : offset_x);
	insize.bits.height = src_height - 1 +
			((src_fmt & WHC_MASK) ? offset_x : 0);

	outsize.bits.width = dst_width - 1;
	outsize.bits.height = dst_height - 1;

	instride.bits.stride0 = y_stride >> 5;
	instride.bits.stride1 = uv_stride >> 5;

	convertcfg.bits.ofstx = offset_x;
	convertcfg.bits.ofsty = offset_y;
	convertcfg.bits.outstrideen = dst_stride_en;

	if (src_fmt == YUV420)
		convertcfg.bits.infmt = 0;
	else if (src_fmt == YVU420)
		convertcfg.bits.infmt = 1;
	else if (src_fmt == ARGB)
		convertcfg.bits.infmt = 2;
	else if (src_fmt == ARGB_WHC)
		convertcfg.bits.infmt = 3;
	else if (src_fmt == GRAY)
		convertcfg.bits.infmt = 4;
	else if (src_fmt == GRAY_WHC)
		convertcfg.bits.infmt = 5;

	if (dst_fmt == YUV420)
		convertcfg.bits.outfmt = 0;
	else if (dst_fmt == YVU420)
		convertcfg.bits.outfmt = 1;
	else if (dst_fmt == ARGB)
		convertcfg.bits.outfmt = 2;
	else if (dst_fmt == ARGB_WHC)
		convertcfg.bits.outfmt = 3;
	else if (dst_fmt == GRAY)
		convertcfg.bits.outfmt = 4;
	else if (dst_fmt == GRAY_WHC)
		convertcfg.bits.outfmt = 5;

	convertcfg.bits.inbw = (inbw == 16 ? 1 : 0);
	convertcfg.bits.outbw = (outbw == 16 ? 1 : 0);
	convertcfg.bits.inlmt = yuv_limit_range;
	convertcfg.bits.tflag = whc_flag;

	facw.bits.fac = (unsigned int)(scalex * (1 << 24));
	fach.bits.fac = (unsigned int)(scaley * (1 << 24));

	if (dst_stride_en)
		outstride.bits.stride0 = dst_stride >> 5;
	else
		outstride.bits.stride0 = 0;

	histroi0.bits.x = hist_roi_x;
	histroi0.bits.y = hist_roi_y;
	histroi1.bits.ex = hist_roi_x_end;
	histroi1.bits.ey = hist_roi_y_end;
	histaddr.bits.addr = hist_result_addr;

	register_group[0] = inaddr0.dwval;
	register_group[1] = inaddr1.dwval;
	register_group[2] = outaddr0.dwval;
	register_group[3] = outaddr1.dwval;
	register_group[4] = insize.dwval;
	register_group[5] = instride.dwval;
	register_group[6] = convertcfg.dwval;
	register_group[7] = 0;
	register_group[8] = outsize.dwval;
	register_group[9] = facw.dwval;
	register_group[10] = fach.dwval;
	register_group[11] = 0;
	register_group[12] = histroi0.dwval;
	register_group[13] = histroi1.dwval;
	register_group[14] = outstride.dwval;
	register_group[15] = histaddr.dwval;
}

void AXVU_GetRszReg(int root_w, int root_h, int yuv_limit_range, int input_fmt,
	int output_fmt, int bw, int outbw,
	int dst_w, int dst_h, int dst_stride_sw, int offset_x, int offset_y,
	int crop_width, int crop_height,
	int merge_mode, int hist_en,
	unsigned int *register_group, unsigned int *register_group_merge,
	unsigned int src_data_addr, unsigned int src_uv_data_addr,
	unsigned int dst_data_addr, unsigned int dst_uv_data_addr,
	unsigned int src_2nd_data_addr,
	unsigned int hist_result_addr, unsigned int hist_roi_x,
	unsigned int hist_roi_y,
	unsigned int hist_roi_x_end, unsigned int hist_roi_y_end,
	int ddr_type, int in_align_byte, int force_out_64B_align)
{
	//int root_rgbstride;
	AXVU_FMT_E src_format, src_format_type;
	AXVU_FMT_E src_format_2nd, src_format_type_2nd;
	AXVU_FMT_E dst_format, dst_format_type;
	bool whc_flag;
	int byte_cnt, out_byte_cnt;
	int byte_align, out_byte_align;
	int src_w1, src_h1, ofs_x1, ofs_y1, crop_w1, crop_h1, dst_w1, dst_h1;
	int src_stride, dst_stride;
	int oft_x_align = 0, oft_y_align = 0;
	unsigned int crop_addr0, crop_addr1;
	int crop_w_align, crop_h_align;
	int crop_w_align_byte, crop_w_align_csc_byte;
	int oft_x_left = 0, oft_y_left = 0, crop_w1_byte = 0;
	//int sBits = 24;
	int dst_stride_en = 0;

	int byte_align_2nd;
	int src_stride_2nd = 0;
	int src_w1_2nd, src_h1_2nd;

	float scalex = (float)crop_width / (float)dst_w;
	float scaley = (float)crop_height / (float)dst_h;

	src_format = (AXVU_FMT_E)input_fmt;
	dst_format = (AXVU_FMT_E)output_fmt;
	src_format_type = (AXVU_FMT_E)(src_format & TYPE_MASK);
	dst_format_type = (AXVU_FMT_E)(dst_format & TYPE_MASK);

	/* reverse W and H for WHC format */
	src_w1 = (src_format & WHC_MASK) ? root_h : root_w;
	src_h1 = (src_format & WHC_MASK) ? root_w : root_h;
	ofs_x1 = (src_format & WHC_MASK) ? offset_y : offset_x;
	ofs_y1 = (src_format & WHC_MASK) ? offset_x : offset_y;
	crop_w1 = (src_format & WHC_MASK) ? crop_height : crop_width;
	crop_h1 = (src_format & WHC_MASK) ? crop_width : crop_height;
	dst_w1 = (dst_format & WHC_MASK) ? dst_h : dst_w;
	dst_h1 = (dst_format & WHC_MASK) ? dst_w : dst_h;

	byte_align = in_align_byte;
	//if (merge_mode > 0)
	//	out_byte_align = ((ddr_type == 1) ? 64 : 32);
	//else
	//	out_byte_align = force_out_64B_align ? 64 :
	//	(ddr_type == 1) ? 64 : 32;
	out_byte_align = force_out_64B_align ? 64 : (ddr_type == 1) ? 64 : 32;

	whc_flag = (bool)((src_format & WHC_MASK) ^ (dst_format & WHC_MASK));

	byte_cnt = (bw + 7) / 8;
	out_byte_cnt = (outbw + 7) / 8;


	// calc sw/hw crop and stride
	AXVU_RszParaConvertInput(root_w, root_h, offset_x, offset_y,
		crop_width, crop_height,
		(AXVU_FMT_E)(src_format & (WHC_MASK | TYPE_MASK)),
		(AXVU_FMT_E)(dst_format & (WHC_MASK | TYPE_MASK)),
		byte_cnt, out_byte_cnt, byte_align,
		src_data_addr, src_uv_data_addr,
		&src_stride,
		&oft_x_align, &oft_y_align, &crop_addr0, &crop_addr1,
		&crop_w_align, &crop_h_align, &crop_w_align_byte,
		&crop_w_align_csc_byte,
		&oft_x_left, &oft_y_left, &crop_w1_byte);

	AXVU_RszParaConvertOutput(dst_w, dst_h, dst_format, out_byte_cnt,
		out_byte_align, &dst_stride);

	if (dst_stride_sw) { // support dst stride 64 Byte align,(HC or WC)
		dst_stride = dst_stride_sw;
		dst_stride_en = 1;
	}

	if (merge_mode != 0) {
		src_format_2nd = merge_mode == 1 ? GRAY : ARGB;
		src_format_type_2nd = (AXVU_FMT_E)(src_format_2nd & TYPE_MASK);

		byte_align_2nd = force_out_64B_align ? 64 :
				(ddr_type == 1) ? 64 : 32;

		src_w1_2nd = (src_format_2nd & WHC_MASK) ? dst_h : dst_w;
		src_h1_2nd = (src_format_2nd & WHC_MASK) ? dst_w : dst_h;

		AXVU_RszParaConvertOutput(dst_w, dst_h, src_format_2nd,
			byte_cnt, byte_align_2nd, &src_stride_2nd);
	}

	AXVU_SetRegRszBatch(crop_width, crop_height, src_format, src_stride,
		src_stride, dst_w, dst_h, dst_format, dst_stride_en, dst_stride,
		scalex, scaley, oft_x_left, oft_y_left,
		whc_flag, yuv_limit_range, bw, outbw, crop_addr0, crop_addr1,
		dst_data_addr, dst_uv_data_addr,
		hist_roi_x, hist_roi_y, hist_roi_x_end, hist_roi_y_end,
		hist_result_addr, register_group);

	if (merge_mode) {
		AXVU_SetRegRszMerge(merge_mode, src_stride_2nd, dst_stride,
			src_2nd_data_addr, register_group_merge);
	}

}
#endif
